package com.lm.generator.util;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.lm.common.core.constant.CommonConstants;
import com.lm.common.core.exception.CheckedException;
import com.lm.generator.entity.ColumnInfo;
import com.lm.generator.entity.GenQo;
import com.lm.generator.entity.TableInfo;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;

import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * 代码生成，基于velocity模版
 *
 * @author lm
 * @date 2019/7/31 09:56
 */
@Slf4j
@UtilityClass
public class GenUtils {

    private static final String VM_JAVA_ENTITY = "Entity.java.vm";
    private static final String VM_JAVA_MAPPER = "Mapper.java.vm";
    private static final String VM_JAVA_SERVICE = "Service.java.vm";
    private static final String VM_JAVA_SERVICE_IMPL = "ServiceImpl.java.vm";
    private static final String VM_JAVA_CONTROLLER = "Controller.java.vm";
    private static final String VM_XML_MAPPER = "Mapper.xml.vm";
    private static final String VM_SQL_MENU = "menu.sql.vm";
    private static final String VM_VUE_INDEX = "index.vue.vm";
    private static final String VM_JS_API = "api.js.vm";
    private static final String VM_JS_CRUD = "crud.js.vm";

    /**
     * 项目空间路径
     */
    private static final String MAVEN_PATH = "/src/main/java/";

    /**
     * 项目空间路径
     */
    private static final String PROJECT_PATH = getProjectPath();

    /**
     * mybatis空间路径
     */
    private static final String MYBATIS_PATH = "main/resources/mapper";

    /**
     * html空间路径
     */
    private static final String TEMPLATE_PATH = "main/resources/template";

    /**
     * 类型转换
     */
    public static Map<String, String> javaTypeMap = new HashMap<String, String>();

    static {
        javaTypeMap.put("tinyint", "Integer");
        javaTypeMap.put("smallint", "Integer");
        javaTypeMap.put("mediumint", "Integer");
        javaTypeMap.put("int", "Integer");
        javaTypeMap.put("integer", "integer");
        javaTypeMap.put("bigint", "Long");
        javaTypeMap.put("float", "Float");
        javaTypeMap.put("double", "Double");
        javaTypeMap.put("decimal", "BigDecimal");
        javaTypeMap.put("bit", "Boolean");
        javaTypeMap.put("char", "String");
        javaTypeMap.put("varchar", "String");
        javaTypeMap.put("tinytext", "String");
        javaTypeMap.put("text", "String");
        javaTypeMap.put("mediumtext", "String");
        javaTypeMap.put("longtext", "String");
        javaTypeMap.put("time", "Date");
        javaTypeMap.put("date", "Date");
        javaTypeMap.put("datetime", "Date");
        javaTypeMap.put("timestamp", "Date");
    }

    /**
     * 生成代码
     *
     * @param table
     * @param columns
     * @param gq
     * @param zip
     */
    public void generatorCode(TableInfo table, List<ColumnInfo> columns, GenQo gq, ZipOutputStream zip) {
        // 配置信息
        Configuration config = getConfig();
        String tablePrefix = StrUtil.isBlank(gq.getTablePrefix()) ? config.getString("tablePrefix") : gq.getTablePrefix();
        gq.setTablePrefix(tablePrefix);
        String moduleName = StrUtil.isBlank(gq.getModuleName()) ? config.getString("moduleName") : gq.getModuleName();
        gq.setModuleName(moduleName);
        String author = StrUtil.isBlank(gq.getAuthor()) ? config.getString("author") : gq.getAuthor();
        gq.setAuthor(author);
        String packageName = StrUtil.isBlank(gq.getPackageName()) ? config.getString("packageName") : gq.getPackageName();
        gq.setPackageName(packageName);

        // 表名转换成Java属性名
        String className = tableToJava(table.getTableName(), gq);
        table.setClassName(className);
        table.setClassname(StringUtils.uncapitalize(className));
        // 列信息
        table.setColumns(transColums(columns));
        // 设置主键
        table.setPrimaryKey(table.getColumnsLast());
        VelocityInitializer.initVelocity();
        VelocityContext context = getVelocityContext(table, gq);
        // 获取模板列表
        List<String> templates = getTemplates(gq);
        for (String template : templates) {
            // 渲染模板
            StringWriter sw = new StringWriter();
            Template tpl = Velocity.getTemplate(template, CommonConstants.UTF8);
            tpl.merge(context, sw);
            try {
                String filename = GenUtils.getFileName(template, table, gq);
                // 添加到zip
                zip.putNextEntry(new ZipEntry(filename));
                IOUtils.write(sw.toString(), zip, CommonConstants.UTF8);
                IOUtils.closeQuietly(sw);
                zip.closeEntry();
            } catch (IOException e) {
                log.error("渲染模板失败，表名：" + table.getTableName(), e);
            }
        }
    }

    /**
     * 设置列信息
     *
     * @param columns
     * @return
     */
    public static List<ColumnInfo> transColums(List<ColumnInfo> columns) {
        // 列信息
        List<ColumnInfo> columsList = new ArrayList<>();
        for (ColumnInfo column : columns) {
            // 列名转换成Java属性名
            String attrName = StrUtil.toCamelCase(column.getColumnName());
            column.setAttrName(attrName);
            column.setAttrname(StringUtils.uncapitalize(attrName));
            // 列的数据类型，转换成Java类型
            String attrType = javaTypeMap.get(column.getDataType());
            column.setAttrType(attrType);
            columsList.add(column);
        }
        return columsList;
    }

    /**
     * 获取模板信息
     *
     * @param table
     * @param gq
     * @return
     */
    public static VelocityContext getVelocityContext(TableInfo table, GenQo gq) {
        // java对象数据传递到模板文件vm
        VelocityContext velocityContext = new VelocityContext();
        String packageName = gq.getPackageName();
        velocityContext.put("tableName", table.getTableName());
        velocityContext.put("tableComment", replaceKeyword(table.getTableComment()));
        velocityContext.put("primaryKey", table.getPrimaryKey());
        velocityContext.put("className", table.getClassName());
        velocityContext.put("classname", table.getClassname());
        velocityContext.put("moduleName", gq.getModuleName());
        //velocityContext.put("moduleName", getModuleName(packageName));
        velocityContext.put("columns", table.getColumns());
        velocityContext.put("basePackage", getBasePackage(packageName));
        velocityContext.put("package", packageName);
        //velocityContext.put("package", packageName + "." + gq.getModuleName());
        velocityContext.put("author", gq.getAuthor());
        velocityContext.put("datetime", DateUtil.formatDateTime(new Date()));

        return velocityContext;
    }

    /**
     * 获取模板信息
     *
     * @param gq
     * @return
     */
    public static List<String> getTemplates(GenQo gq) {
        List<String> templates = new ArrayList<String>();
        templates.add("vm/java/" + VM_JAVA_ENTITY);
        templates.add("vm/java/" + VM_JAVA_MAPPER);
        templates.add("vm/java/" + VM_JAVA_SERVICE);
        templates.add("vm/java/" + VM_JAVA_SERVICE_IMPL);
        templates.add("vm/java/" + VM_JAVA_CONTROLLER);
        // templates.add("vm/java/Client.java.vm");
        // templates.add("vm/java/ClientFallback.java.vm");
        // templates.add("vm/java/ClientController.java.vm");
        templates.add("vm/xml/" + VM_XML_MAPPER);
        //if (gq.getSqlSwitch()) {
        templates.add("vm/sql/" + VM_SQL_MENU);
        //}
        //if (gq.getPageSwitch()) {
        templates.add("vm/html/list.html.vm");
        templates.add("vm/html/add.html.vm");
        templates.add("vm/html/edit.html.vm");
        //}
        return templates;
    }

    /**
     * 表名转换成Java类名
     *
     * @param tableName 表名
     * @param gq
     * @return
     */
    public static String tableToJava(String tableName, GenQo gq) {
        if (!StringUtils.isBlank(gq.getTablePrefix())) {
            tableName = tableName.replaceFirst(gq.getTablePrefix(), "");
        }
        return StrUtil.upperFirst(StrUtil.toCamelCase(tableName));
    }

    /**
     * 获取配置信息
     */
    private Configuration getConfig() {
        try {
            return new PropertiesConfiguration("generator.properties");
        } catch (ConfigurationException e) {
            throw new CheckedException("获取配置文件失败，", e);
        }
    }

    /**
     * 获取文件名
     *
     * @param template
     * @param table
     * @param gq
     * @return
     */
    public static String getFileName(String template, TableInfo table, GenQo gq) {
        // 首字母小写类名
        String classname = table.getClassname();
        // 首字母大写类名
        String className = table.getClassName();
        String moduleName = gq.getModuleName();

        String javaPath = PROJECT_PATH;
        String mybatisPath = MYBATIS_PATH + "/" + moduleName + "/" + className;
        String htmlPath = TEMPLATE_PATH + "/" + moduleName + "/" + classname;

        if (template.contains(VM_JAVA_ENTITY)) {
            return javaPath + "entity" + File.separator + className + ".java";
        }
        if (template.contains(VM_JAVA_MAPPER)) {
            return javaPath + "mapper" + File.separator + className + "Mapper.java";
        }
        if (template.contains(VM_JAVA_SERVICE)) {
            return javaPath + "service" + File.separator + "I" + className + "Service.java";
        }
        if (template.contains(VM_JAVA_SERVICE_IMPL)) {
            return javaPath + "service" + File.separator + "impl" + File.separator + className + "ServiceImpl.java";
        }
        if (template.contains(VM_JAVA_CONTROLLER)) {
            return javaPath + "controller" + File.separator + className + "Controller.java";
        }

        if (template.contains(VM_XML_MAPPER)) {
            return mybatisPath + File.separator + className + "Mapper.xml";
        }
        if (template.contains(VM_SQL_MENU)) {
            return classname + "_Menu.sql";
        }

//        if (template.contains(INDEX_VUE_VM)) {
//            return CommonConstants.FRONT_END_PROJECT + File.separator + "src" + File.separator + "views" +
//                    File.separator + moduleName + File.separator + className.toLowerCase() + File.separator + "index.vue";
//        }
//        if (template.contains(API_JS_VM)) {
//            return CommonConstants.FRONT_END_PROJECT + File.separator + "src" + File.separator + "api" + File.separator + moduleName + File.separator + className.toLowerCase() + ".js";
//        }
//        if (template.contains(CRUD_JS_VM)) {
//            return CommonConstants.FRONT_END_PROJECT + File.separator + "src" + File.separator + "const" +
//                    File.separator + "crud" + File.separator + moduleName + File.separator + className.toLowerCase() + ".js";
//        }

        // if (template.contains("Client.java.vm"))
        // {
        // return javaPath + "feign" + "/" + "I" + className + "Client.java";
        // }
        // if (template.contains("ClientFallback.java.vm"))
        // {
        // return javaPath + "feign" + "/" + "factory/" + className +
        // "ClientFallbackFactory.java";
        // }
        // if (template.contains("ClientController.java.vm"))
        // {
        // return javaPath + "client" + "/" + className + "Client.java";
        // }

        if (template.contains("list.html.vm")) {
            return htmlPath + "/" + classname + ".html";
        }
        if (template.contains("add.html.vm")) {
            return htmlPath + "/add.html";
        }
        if (template.contains("edit.html.vm")) {
            return htmlPath + "/edit.html";
        }

        return null;
    }

    /**
     * 获取模块名
     *
     * @param packageName
     * @return
     */
    public static String getModuleName(String packageName) {
        int lastIndex = packageName.lastIndexOf(".");
        int nameLength = packageName.length();
        String moduleName = StringUtils.substring(packageName, lastIndex + 1, nameLength);
        return moduleName;
    }

    public static String getBasePackage(String packageName) {
        int lastIndex = packageName.lastIndexOf(".");
        String basePackage = StringUtils.substring(packageName, 0, lastIndex);
        return basePackage;
    }

    public static String getProjectPath() {
        Configuration config = getConfig();
        String packageName = config.getString("packageName");
        StringBuffer projectPath = new StringBuffer();
        projectPath.append("main/java/");
        projectPath.append(packageName.replace(".", "/"));
        projectPath.append("/");
        return projectPath.toString();
    }

    public static String replaceKeyword(String keyword) {
        String keyName = keyword.replaceAll("(?:表|信息)", "");
        return keyName;
    }

}
